#include "sprite.h"
#include "director.h"

namespace jlib {


void ImageNode::draw (const mat4 &pv, const mat4 &p, const mat4 &v, const mat4 &m) {
  if (!this->_texFrag)
    return;
  auto tex = this->_texFrag->tex();
  if (!tex || !tex->valid())
    return;
  if (this->_imageNodeFlags & VerticesAndTexCoordDirty)
    this->resolveVerticesAndTexCoord();
  tex->bind(u_tex);
  this->_shader->use();
  this->_shader->u4m(u_pvm, pv * m);
  this->_shader->u4f(u_color, this->_color);
  glEnableVertexAttribArray(a_position);
  glEnableVertexAttribArray(a_texCoord);
  glVertexAttribPointer(a_position, 2, GL_FLOAT, GL_FALSE, 0, this->_vertices);
  glVertexAttribPointer(a_texCoord, 2, GL_FLOAT, GL_FALSE, 0, this->_texCoord);
  glBlendFunc(this->_srcFactor, this->_dstFactor);
  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}



bool ImageNode::init (TexFrag *texFrag, Shader *shader) noexcept {
  if (this->_texFrag = texFrag)
    this->_imageNodeFlags = VerticesAndTexCoordDirty;

  if (shader)
    this->_shader = shader;
  if (!this->_shader)
    this->_shader = this->_root->getShaderManager()->get(ShaderName, VShader, FShader);
  return this->_shader && this->_shader->valid();
}

bool ImageNode::init (Texture *texture, Shader *shader) noexcept {
  TexFrag *texFrag = texture ? Object::create<TexFrag>(texture) : nullptr;
  return this->init(texFrag, shader);
}

bool ImageNode::init (const std::string &path, FileDomain domain, Shader *shader) noexcept {
  auto texture = this->_root->getTextureManager()->get(path, domain);
  if (!texture)
    return false;
  auto texFrag = Object::create<TexFrag>(texture);
  return this->init(texFrag, shader);
}

void ImageNode::clear () noexcept {
  this->_texFrag = nullptr;
  this->_shader = nullptr;
}



void ImageNode::setShader (Shader *shader) noexcept {
  if (!shader || !shader->valid())
    return;
  this->_shader = shader;
}

void ImageNode::setTexFrag (TexFrag *texFrag) noexcept {
  this->_texFrag = texFrag;
}

void ImageNode::setTexture (Texture *texture) noexcept {
  this->_texFrag = Object::create<TexFrag>(texture);
}

void ImageNode::setColor (const rgba &color) noexcept {
  this->_color = color;
}

void ImageNode::setBlendFunc (GLenum src, GLenum dst) noexcept {
  this->_srcFactor = src;
  this->_dstFactor = dst;
}

void ImageNode::setAnchor (const vec2 &anchor) noexcept {
  this->_anchor = anchor;
  this->_imageNodeFlags |= VerticesAndTexCoordDirty;
}

void ImageNode::setAnchor (float x, float y) noexcept {
  this->_anchor = vec2(x, y);
  this->_imageNodeFlags |= VerticesAndTexCoordDirty;
}

void ImageNode::setAnchorX (float x) noexcept {
  this->_anchor.x = x;
  this->_imageNodeFlags |= VerticesAndTexCoordDirty;
}

void ImageNode::setAnchorY (float y) noexcept {
  this->_anchor.y = y;
  this->_imageNodeFlags |= VerticesAndTexCoordDirty;
}



void ImageNode::resolveVerticesAndTexCoord () noexcept {
  auto frame = this->_texFrag;
  auto tex = frame->tex();
  auto size = tex->sizef();
  auto min = -this->_anchor * size;
  auto max = min + size;
  this->_vertices[0] = min;
  this->_vertices[1] = {max.x, min.y};
  this->_vertices[2] = {min.x, max.y};
  this->_vertices[3] = max;
  auto &box = frame->rect();
  this->_texCoord[0] = box.dmin;
  this->_texCoord[1] = {box.max.x, box.min.y};
  this->_texCoord[2] = {box.min.x, box.max.y};
  this->_texCoord[3] = box.dmax;
  this->_imageNodeFlags &= ~VerticesAndTexCoordDirty;
}


const char *ImageNode::VShader = R"(#version 430 core
layout(location = 0) uniform mat4 u_pvm;
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main () {
  v_texCoord = a_texCoord;
  gl_Position = u_pvm * a_position;
}
)";

const char *ImageNode::FShader = R"(#version 430 core
layout(binding = 0) uniform sampler2D u_tex;
layout(location = 1) uniform vec4 u_color;
in vec2 v_texCoord;
void main () {
  gl_FragColor = texture2D(u_tex, v_texCoord) * u_color;
}
)";

const std::string ImageNode::ShaderName = "ImageNode::default";


} // namespace jlib
